home *** CD-ROM | disk | FTP | other *** search
/ Java Programmer's Toolkit / Java Programmer's Toolkit.iso / gs3.53 / bdftops.ps < prev    next >
Text File  |  1996-01-10  |  24KB  |  794 lines

  1. %    Copyright (C) 1990, 1995 Aladdin Enterprises.  All rights reserved.
  2. % This file is part of Aladdin Ghostscript.
  3. % Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  4. % or distributor accepts any responsibility for the consequences of using it,
  5. % or for whether it serves any particular purpose or works at all, unless he
  6. % or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  7. % License (the "License") for full details.
  8. % Every copy of Aladdin Ghostscript must include a copy of the License,
  9. % normally in a plain ASCII text file named PUBLIC.  The License grants you
  10. % the right to copy, modify and redistribute Aladdin Ghostscript, but only
  11. % under certain conditions described in the License.  Among other things, the
  12. % License requires that the copyright notice and this notice be preserved on
  13. % all copies.
  14.  
  15. % bdftops.ps
  16. % Convert a BDF file (possibly with (an) associated AFM file(s))
  17. % to a PostScript Type 1 font (without eexec encryption).
  18. % The resulting font will work with any PostScript language interpreter,
  19. % but not with ATM or other font rasterizers lacking a complete interpreter.
  20.  
  21. /envBDF 120 dict def
  22. envBDF begin
  23.  
  24. % "Import" the image-to-path package.
  25. % This also brings in the Type 1 opcodes (type1ops.ps).
  26.    (impath.ps) run
  27.  
  28. % "Import" the font-writing package.
  29.    (wrfont.ps) run
  30.    wrfont_dict begin
  31.      /binary_CharStrings false def
  32.      /binary_tokens false def
  33.      /encrypt_CharStrings true def
  34.      /standard_only true def
  35.    end
  36.    /lenIV 0 def
  37.  
  38. % Invert the StandardEncoding vector.
  39.    256 dict dup begin
  40.    0 1 255 { dup StandardEncoding exch get exch def } for
  41.    end /StandardDecoding exch def
  42.  
  43. % Define the properties copied to FontInfo.
  44.    mark
  45.      (COPYRIGHT) /Notice
  46.      (FAMILY_NAME) /FamilyName
  47.      (FULL_NAME) /FullName
  48.      (WEIGHT_NAME) /Weight
  49.    .dicttomark /properties exch def
  50.  
  51. % Define the character sequences for synthesizing missing composite
  52. % characters in the standard encoding.
  53.    mark
  54.      /AE [/A /E]
  55.      /OE [/O /E]
  56.      /ae [/a /e]
  57.      /ellipsis [/period /period /period]
  58.      /emdash [/hyphen /hyphen /hyphen]
  59.      /endash [/hyphen /hyphen]
  60.      /fi [/f /i]
  61.      /fl [/f /l]
  62.      /germandbls [/s /s]
  63.      /guillemotleft [/less /less]
  64.      /guillemotright [/greater /greater]
  65.      /oe [/o /e]
  66.      /quotedblbase [/comma /comma]
  67.    .dicttomark /composites exch def
  68.  
  69. % Define the procedure for synthesizing composites.
  70. % This must not be bound.
  71.    /compose
  72.     { exch pop
  73.       FontMatrix Private /composematrix get invertmatrix concat
  74.       0 0 moveto
  75.       dup gsave false charpath pathbbox currentpoint grestore
  76.       6 2 roll setcachedevice show
  77.     } def
  78. % Define the CharString procedure that calls compose, with the string
  79. % on the stack.  This too must remain unbound.
  80.    /compose_proc
  81.     { Private /compose get exec
  82.     } def
  83.  
  84. % Define aliases for missing characters similarly.
  85.    mark
  86.      /acute /quoteright
  87.      /bullet /asterisk
  88.      /cedilla /comma
  89.      /circumflex /asciicircum
  90.      /dieresis /quotedbl
  91.      /dotlessi /i
  92.      /exclamdown /exclam
  93.      /florin /f
  94.      /fraction /slash
  95.      /grave /quoteleft
  96.      /guilsinglleft /less
  97.      /guilsinglright /greater
  98.      /hungarumlaut /quotedbl
  99.      /periodcentered /asterisk
  100.      /questiondown /question
  101.      /quotedblleft /quotedbl
  102.      /quotedblright /quotedbl
  103.      /quotesinglbase /comma
  104.      /quotesingle /quoteright
  105.      /tilde /asciitilde
  106.    .dicttomark /aliases exch def
  107.  
  108. % Define overstruck characters that can be synthesized with seac.
  109.    mark
  110.     [ /Aacute /Acircumflex /Adieresis /Agrave /Aring /Atilde
  111.       /Ccedilla
  112.       /Eacute /Ecircumflex /Edieresis /Egrave
  113.       /Iacute /Icircumflex /Idieresis /Igrave
  114.       /Lslash
  115.       /Ntilde
  116.       /Oacute /Ocircumflex /Odieresis /Ograve /Otilde
  117.       /Scaron
  118.       /Uacute /Ucircumflex /Udieresis /Ugrave
  119.       /Yacute /Ydieresis
  120.       /Zcaron
  121.       /aacute /acircumflex /adieresis /agrave /aring /atilde
  122.       /ccedilla
  123.       /eacute /ecircumflex /edieresis /egrave
  124.       /iacute /icircumflex /idieresis /igrave
  125.       /lslash
  126.       /ntilde
  127.       /oacute /ocircumflex /odieresis /ograve /otilde
  128.       /scaron
  129.       /uacute /ucircumflex /udieresis /ugrave
  130.       /yacute /ydieresis
  131.       /zcaron
  132.     ]
  133.     { dup =string cvs
  134.       [ exch dup 0 1 getinterval cvn
  135.     exch dup length 1 sub 1 exch getinterval cvn
  136.       ]
  137.     } forall
  138.      /cent [/c /slash]
  139.      /daggerdbl [/bar /equal]
  140.      /divide [/colon /hyphen]
  141.      /sterling [/L /hyphen]
  142.      /yen [/Y /equal]
  143.    .dicttomark /accentedchars exch def
  144.  
  145. % ------ Output utilities ------ %
  146.  
  147.    /ws {psfile exch writestring} bind def
  148.    /wl {ws (\n) ws} bind def
  149.    /wt {=string cvs ws ( ) ws} bind def
  150.  
  151. % ------ BDF file parsing utilities ------ %
  152.  
  153. % Define a buffer for reading the BDF file.
  154.    /buffer 400 string def
  155.  
  156. % Read a line from the BDF file into the buffer.
  157. % Ignore empty (zero-length) lines.
  158. % Define /keyword as the first word on the line.
  159. % Define /args as the remainder of the line.
  160. % If the keyword is equal to commentword, skip the line.
  161. % (If commentword is equal to a space, never skip.)
  162.    /nextline
  163.     {  { bdfile buffer readline not
  164.       { (Premature EOF\n) print stop } if
  165.      dup length 0 ne { exit } if pop     
  166.        }
  167.       loop
  168.       ( ) search
  169.        { /keyword exch def pop }
  170.        { /keyword exch def () }
  171.       ifelse
  172.       /args exch def
  173.       keyword commentword eq { nextline } if
  174.     } bind def
  175.  
  176. % Get a word argument from args.  We do *not* copy the string.
  177.    /warg        % warg -> string
  178.     { args ( ) search
  179.        { exch pop exch }
  180.        { () }
  181.       ifelse  /args exch def
  182.     } bind def
  183.  
  184. % Get an integer argument from args.
  185.    /iarg        % iarg -> int
  186.     { warg cvi
  187.     } bind def
  188.  
  189. % Get a numeric argument from args.
  190.    /narg        % narg -> int|real
  191.     { warg cvr
  192.       dup dup cvi eq { cvi } if
  193.     } bind def
  194.  
  195. % Convert the remainder of args into a string.
  196.    /remarg        % remarg -> string
  197.     { args copystring
  198.     } bind def
  199.  
  200. % Get a string argument that occupies the remainder of args.
  201.    /sarg        % sarg -> string
  202.     { args (") anchorsearch
  203.        { pop /args exch def } { pop } ifelse
  204.       args args length 1 sub get (") 0 get eq
  205.        { args 0 args length 1 sub getinterval /args exch def } if
  206.       args copystring
  207.     } bind def
  208.  
  209. % Check that the keyword is the expected one.
  210.    /checkline        % (EXPECTED-KEYWORD) checkline ->
  211.     { dup keyword ne
  212.        { (Expected ) print =
  213.          (Line=) print keyword print ( ) print args print (\n) print stop
  214.        } if
  215.       pop
  216.     } bind def
  217.  
  218. % Read a line and check its keyword.
  219.    /getline        % (EXPECTED-KEYWORD) getline ->
  220.     { nextline checkline
  221.     } bind def
  222.  
  223. % Find the first/last non-zero bit of a non-zero byte.
  224.    /fnzb
  225.     { 0 { exch dup 128 ge { pop exit } { dup add exch 1 add } ifelse }
  226.       loop
  227.     } bind def
  228.    /lnzb
  229.     { 7 { exch dup 1 and 0 ne { pop exit } { -1 bitshift exch 1 sub } ifelse }
  230.       loop
  231.     } bind def
  232.  
  233. % ------ Type 1 encoding utilities ------ %
  234.  
  235. % Parse the side bearing and width information that begins a CharString.
  236. % Arguments: charstring.  Result: sbx sby wx wy substring.
  237.    /parsesbw
  238.     { mark exch lenIV
  239.        {        % stack: mark ... string dropcount
  240.          dup 2 index length exch sub getinterval
  241.      dup 0 get dup 32 lt { pop exit } if
  242.      dup 246 le
  243.       { 139 sub exch 1 }
  244.       { dup 250 le
  245.          { 247 sub 8 bitshift 108 add 1 index 1 get add exch 2 }
  246.          { dup 254 le
  247.         { 251 sub 8 bitshift 108 add 1 index 1 get add neg exch 2 }
  248.         { pop dup 1 get 128 xor 128 sub
  249.           8 bitshift 1 index 2 get add
  250.           8 bitshift 1 index 3 get add
  251.           8 bitshift 1 index 4 get add exch 5
  252.         } ifelse
  253.          } ifelse
  254.       } ifelse
  255.        } loop
  256.       counttomark 3 eq { 0 3 1 roll 0 exch } if
  257.       6 -1 roll pop
  258.     } bind def 
  259.  
  260. % Find the side bearing and width information that begins a CharString.
  261. % Arguments: charstring.  Result: charstring sizethroughsbw.
  262.    /findsbw
  263.     { dup parsesbw 4 { exch pop } repeat skipsbw
  264.     } bind def
  265.    /skipsbw        % charstring sbwprefix -> sizethroughsbw
  266.     { length 1 index length exch sub
  267.       2 copy get 12 eq { 2 } { 1 } ifelse add
  268.     } bind def
  269.  
  270. % Encode a number, and append it to a string.
  271. % Arguments: str num.  Result: newstr.
  272.    /concatnum
  273.     { dup dup -107 ge exch 107 le and
  274.        { 139 add 1 string dup 0 3 index put }
  275.        { dup dup -1131 ge exch 1131 le and
  276.           { dup 0 ge { 16#f694 } { neg 16#fa94 } ifelse add
  277.         2 string dup 0 3 index -8 bitshift put
  278.         dup 1 3 index 255 and put
  279.       }
  280.       { 5 string dup 0 255 put exch
  281.         2 copy 1 exch -24 bitshift 255 and put
  282.         2 copy 2 exch -16 bitshift 255 and put
  283.         2 copy 3 exch -8 bitshift 255 and put
  284.         2 copy 4 exch 255 and put
  285.         exch
  286.       }
  287.      ifelse
  288.        }
  289.       ifelse exch pop concatstrings
  290.     } bind def
  291.  
  292. % ------ Point arithmetic utilities ------ %
  293.  
  294.    /ptadd { exch 4 -1 roll add 3 1 roll add } bind def
  295.    /ptexch { 4 2 roll } bind def
  296.    /ptneg { neg exch neg exch } bind def
  297.    /ptpop { pop pop } bind def
  298.    /ptsub { ptneg ptadd } bind def
  299.  
  300. % ------ The main program ------ %
  301.  
  302.    /readBDF        % <infilename> <outfilename> <fontname>
  303.             %   <encodingname> <uniqueID> <xuid> readBDF -> <font>
  304.     { /xuid exch def        % may be null
  305.       /uniqueID exch def    % may be -1
  306.       /encodingname exch def
  307.     /encoding encodingname cvx exec def
  308.       /fontname exch def
  309.       /psname exch def
  310.       /bdfname exch def
  311.       gsave        % so we can set the CTM to the font matrix
  312.  
  313. %  Open the input files.  We don't open the output file until
  314. %  we've done a minimal validity check on the input.
  315.       bdfname (r) file /bdfile exch def
  316.       /commentword ( ) def
  317.  
  318. %  Check for the STARTFONT.
  319.       (STARTFONT) getline
  320.       args (2.1) ne { (Not version 2.1\n) print stop } if
  321.  
  322. %  Initialize the font.
  323.       /Font 20 dict def
  324.       Font begin
  325.       /FontName fontname def
  326.       /PaintType 0 def
  327.       /FontType 1 def
  328.       uniqueID 0 gt { /UniqueID uniqueID def } if
  329.       xuid null ne { /XUID xuid def } if
  330.       /Encoding encoding def
  331.       /FontInfo 20 dict def
  332.       /Private 20 dict def
  333.       currentdict end currentdict end
  334.       exch begin begin        % insert font above environment
  335.  
  336. %  Initialize the Private dictionary in the font.
  337.       Private begin
  338.       /-! {string currentfile exch readhexstring pop} readonly def
  339.       /-| {string currentfile exch readstring pop} readonly def
  340.       /|- {readonly def} readonly def
  341.       /| {readonly put} readonly def
  342.       /BlueValues [] def
  343.       /lenIV lenIV def
  344.       /MinFeature {16 16} def
  345.       /password 5839 def
  346.       /UniqueID uniqueID def
  347.       end        % Private
  348.  
  349. %  Invert the Encoding, for synthesizing composite characters.
  350.       /decoding encoding length dict def
  351.       0 1 encoding length 1 sub
  352.        { dup encoding exch get exch decoding 3 1 roll put }
  353.       for
  354.  
  355. %  Now open the output file.
  356.       psname (w) file /psfile exch def
  357.  
  358. %  Put out a header compatible with the Adobe "standard".
  359.       (%!FontType1-1.0: ) ws fontname wt (000.000) wl
  360.       (% This is a font description converted from ) ws
  361.     bdfname wl
  362.       (%   by bdftops running on ) ws
  363.       statusdict /product get ws ( revision ) ws
  364.       revision =string cvs ws (.) wl
  365.  
  366. %  Copy the initial comments, up to FONT.
  367.       true
  368.        { nextline
  369.      keyword (COMMENT) ne {exit} if
  370.       { (% Here are the initial comments from the BDF file:\n%) wl
  371.       } if false
  372.      (%) ws remarg wl
  373.        } loop pop
  374.       () wl
  375.       /commentword (COMMENT) def    % do skip comments from now on
  376.  
  377. %  Read and process the FONT, SIZE, and FONTBOUNDINGBOX.
  378.       % If we cared about FONT, we'd use it here.  If the BDF files
  379.       % from MIT had PostScript names rather than X names, we would
  380.       % care; but what's there is unusable, so we discard FONT.
  381.       % The FONTBOUNDINGBOX may not be reliable, so we discard it too.
  382.       (FONT) checkline
  383.       (SIZE) getline
  384.     /pointsize iarg def   /xres iarg def   /yres iarg def
  385.       (FONTBOUNDINGBOX) getline
  386.       nextline
  387.  
  388. %  Initialize the font bounding box bookeeping.
  389.       /fbbxo 1000 def
  390.       /fbbyo 1000 def
  391.       /fbbxe -1000 def
  392.       /fbbye -1000 def
  393.  
  394. %  Read and process the properties.  We only care about a few of them.
  395.       keyword (STARTPROPERTIES) eq
  396.        { iarg
  397.           { nextline
  398.         properties keyword known
  399.          { FontInfo properties keyword get sarg readonly put
  400.          } if
  401.       } repeat
  402.          (ENDPROPERTIES) getline
  403.      nextline
  404.        } if
  405.  
  406. %  Compute and set the FontMatrix.
  407.       Font /FontMatrix
  408.        [ 0.001 0 0 0.001 xres mul yres div 0 0 ] readonly
  409.       dup setmatrix put
  410.  
  411. %  Read and process the header for the bitmaps.
  412.       (CHARS) checkline
  413.     /ccount iarg def
  414.  
  415. %  Initialize the CharStrings dictionary.
  416.       /charstrings ccount
  417.     composites length add
  418.     aliases length add
  419.     accentedchars length add
  420.     1 add dict def        % 1 add for .notdef
  421.       /isfixedwidth true def
  422.       /fixedwidth null def
  423.       /subrcount 0 def
  424.       /subrs [] def
  425.  
  426. %  Read the bitmap data.  This reads the remainder of the file.
  427. %  We do this before processing the bitmaps so that we can compute
  428. %  the correct FontBBox first.
  429.       /chardata ccount dict def
  430.       ccount -1 1
  431.        { (STARTCHAR) getline
  432.            /charname remarg def
  433.      (ENCODING) getline
  434.        /eindex iarg def
  435.        eindex 0 ge
  436.         { charname /charname StandardEncoding eindex get def
  437.           charname /.notdef eq eindex 0 gt and
  438.            { /charname (A) eindex =string cvs concatstrings cvn def
  439.            }
  440.           if
  441.           (/) print charname =string cvs print (,) print print
  442.         }
  443.         { (/) print charname print
  444.         }
  445.        ifelse
  446.        10 mod 1 eq { (\n) print flush } if
  447.      (SWIDTH) getline
  448.        /swx iarg pointsize mul 1000 div xres mul 72 div def
  449.        /swy iarg pointsize mul 1000 div xres mul 72 div def
  450.      (DWIDTH) getline        % Ignore, use SWIDTH instead
  451.      (BBX) getline
  452.        /bbw iarg def  /bbh iarg def  /bbox iarg def  /bboy iarg def
  453.      nextline
  454.      keyword (ATTRIBUTES) eq
  455.       { nextline
  456.       } if
  457.      (BITMAP) checkline
  458.  
  459. % Update the font bounding box.
  460.      /fbbxo fbbxo bbox min def
  461.      /fbbyo fbbyo bboy min def
  462.      /fbbxe fbbxe bbox bbw add max def
  463.      /fbbye fbbye bboy bbh add max def
  464.  
  465. % Read the bits for this character.
  466.      /raster bbw 7 add 8 idiv def
  467.      /cbits raster bbh mul string def
  468.      0 raster cbits length raster sub
  469.       { cbits exch raster getinterval
  470.         bdfile buffer readline not
  471.          { (EOF in bitmap\n) print stop } if
  472.         % stack has <cbits.interval> <buffer.interval>
  473.         0 () /SubFileDecode filter
  474.         exch 2 copy readhexstring pop pop pop closefile
  475.       } for
  476.      (ENDCHAR) getline
  477.  
  478. % Save the character data.
  479.      chardata charname [swx swy bbw bbh bbox bboy cbits] put
  480.        } for
  481.  
  482.       (ENDFONT) getline
  483.  
  484. % Allocate the buffers for the bitmap and the outline,
  485. % according to the font bounding box.
  486.       /fbbw fbbxe fbbxo sub def
  487.       /fbbh fbbye fbbyo sub def
  488.       /fraster fbbw 7 add 8 idiv def
  489.       /bits fraster fbbh mul 200 max 65535 min string def
  490.       /outline bits length 6 mul 65535 min string def
  491.  
  492. %  Process the characters.
  493.       chardata
  494.        { exch /charname exch def  aload pop
  495.      /cbits exch def
  496.      /bboy exch def   /bbox exch def
  497.      /bbh exch def   /bbw exch def
  498.      /swy exch def   /swx exch def
  499.  
  500. % The bitmap handed to type1imagepath must have the correct height,
  501. % because type1imagepath uses this to compute the scale factor,
  502. % so we have to clear the unused parts of it.
  503.      /raster bbw 7 add 8 idiv def
  504.      bits dup 0 1 raster fbbh mul 1 sub
  505.       { 0 put dup } for
  506.      pop pop
  507.      bits raster fbbh bbh sub mul cbits putinterval
  508.  
  509. %  Compute the font entry, converting the bitmap to an outline.
  510.      bits 0 raster fbbh mul getinterval    % the bitmap image
  511.      bbw   fbbh                % bitmap width & height
  512.      swx   swy                % width x & y
  513.      bbox neg   bboy neg            % origin x & y
  514.          % Account for lenIV when converting the outline.
  515.      outline  lenIV  outline length lenIV sub  getinterval
  516.      type1imagepath
  517.      length lenIV add
  518.      outline exch 0 exch getinterval
  519.  
  520. % Check for a fixed width font.
  521.      isfixedwidth
  522.       { fixedwidth null eq
  523.          { /fixedwidth swx def }
  524.          { fixedwidth swx ne { /isfixedwidth false def } if }
  525.         ifelse
  526.       } if
  527.  
  528. % Finish up the character.
  529.      copystring
  530.      charname exch charstrings 3 1 roll put
  531.        } forall
  532.  
  533. %  Add CharStrings entries for aliases.
  534.       aliases
  535.        { charstrings 2 index known not charstrings 2 index known and
  536.           { charstrings exch get charstrings 3 1 roll put
  537.       }
  538.       { pop pop
  539.       }
  540.      ifelse
  541.        }
  542.       forall
  543.  
  544. %  If this is not a fixed-width font, synthesize missing characters
  545. %  out of available ones.
  546.       isfixedwidth not
  547.        { false composites
  548.       { 1 index charstrings exch known not
  549.         1 index { decoding exch known and } forall
  550.          { ( /) print 1 index bits cvs print
  551.            /combine exch def
  552.            0 1 combine length 1 sub
  553.         { dup combine exch get decoding exch get
  554.           bits 3 1 roll put
  555.         } for
  556.            bits 0 combine length getinterval copystring
  557.            [ exch /compose_proc load aload pop ] cvx
  558.            charstrings 3 1 roll put
  559.            pop true
  560.          }
  561.          { pop pop }
  562.         ifelse
  563.       }
  564.      forall flush
  565.       { Private /composematrix matrix put
  566.         Private /compose /compose load put
  567.       }
  568.      if
  569.        }
  570.       if
  571.  
  572. %  Synthesize accented characters with seac if needed and possible.
  573.       accentedchars
  574.        { aload pop /accent exch def /base exch def
  575.          buffer cvs /accented exch def
  576.      charstrings accented known not
  577.      charstrings base known and
  578.      charstrings accent known and
  579.      StandardDecoding base known and
  580.      StandardDecoding accent known and
  581.      encoding StandardDecoding base get get base eq and
  582.      encoding StandardDecoding accent get get accent eq and
  583.       { ( /) print accented print
  584.         charstrings base get findsbw 0 exch getinterval
  585.         /acstring exch def        % start with sbw of base
  586.         charstrings accent get parsesbw
  587.         4 { pop } repeat        % just leave sbx
  588.         acstring exch concatnum
  589.         0 concatnum 0 concatnum        % adx ady
  590.         decoding base get concatnum        % bchar
  591.         decoding accent get concatnum    % achar
  592.         s_seac concatstrings
  593.         charstrings exch accented copystring exch put
  594.       } if
  595.        } forall
  596.  
  597. %  Make a CharStrings entry for .notdef.
  598.       outline lenIV <8b8b0d0e> putinterval    % 0 0 hsbw endchar
  599.       charstrings /.notdef outline 0 lenIV 4 add getinterval copystring put
  600.  
  601. %  Encrypt the CharStrings and Subrs (in place).
  602.       charstrings
  603.        {    % Be careful not to encrypt aliased characters twice,
  604.         % since they share their CharString.
  605.      aliases 2 index known
  606.       { charstrings aliases 3 index get .knownget
  607.          { 1 index ne }
  608.          { true }
  609.         ifelse
  610.       }
  611.       { true
  612.       }
  613.      ifelse
  614.      1 index type /stringtype eq and
  615.           { 4330 exch dup .type1encrypt exch pop
  616.         readonly charstrings 3 1 roll put
  617.       }
  618.       { pop pop
  619.       }
  620.      ifelse
  621.        }
  622.       forall
  623.       0 1 subrcount 1 sub
  624.        { dup subrs exch get
  625.      4330 exch dup .type1encrypt exch pop
  626.      subrs 3 1 roll put
  627.        }
  628.       for
  629.  
  630. %  Make most of the remaining entries in the font dictionaries.
  631.  
  632. % The Type 1 font machinery really only works with a 1000 unit
  633. % character coordinate system.  Set this up here, by computing the factor
  634. % to make the X entry in the FontMatrix come out at exactly 0.001.
  635.       /fontscale 1000 fbbh div yres mul xres div def
  636.       Font /FontBBox
  637.        [ fbbxo fontscale mul
  638.      fbbyo fontscale mul
  639.      fbbxe fontscale mul
  640.      fbbye fontscale mul
  641.        ] cvx readonly put
  642.       Font /CharStrings charstrings readonly put
  643.       FontInfo /FullName known not
  644.        { % Some programs insist on FullName being present.
  645.          FontInfo /FullName FontName dup length string cvs put
  646.        }
  647.       if
  648.       FontInfo /isFixedPitch isfixedwidth put
  649.       subrcount 0 gt
  650.        { Private /Subrs subrs 0 subrcount getinterval readonly put
  651.        } if
  652.  
  653. %  Determine the italic angle and underline position
  654. %  by actually installing the font.
  655.       save
  656.       /_temp_ Font definefont setfont
  657.       [1000 0 0 1000 0 0] setmatrix        % mitigate rounding problems
  658. % The italic angle is the multiple of -5 degrees
  659. % that minimizes the width of the 'I'.
  660.       0 9999 0 5 85
  661.        { dup rotate
  662.          newpath 0 0 moveto (I) false charpath
  663.      dup neg rotate
  664.          pathbbox pop exch pop exch sub
  665.      dup 3 index lt { 4 -2 roll } if
  666.      pop pop
  667.        }
  668.       for pop
  669. % The underline position is halfway between the bottom of the 'A'
  670. % and the bottom of the FontBBox.
  671.       newpath 0 0 moveto (A) false charpath
  672.       FontMatrix concat
  673.       pathbbox pop pop exch pop
  674. %  Put the values in FontInfo.
  675.       3 -1 roll
  676.       restore
  677.       Font /FontBBox get 1 get add 2 div cvi
  678.       dup FontInfo /UnderlinePosition 3 -1 roll put
  679.       2 div abs FontInfo /UnderlineThickness 3 -1 roll put
  680.       FontInfo /ItalicAngle 3 -1 roll put
  681.  
  682. %  Clean up and finish.
  683.       grestore
  684.       bdfile closefile
  685.       Font currentdict end end begin        % remove font from dict stack
  686.       (\n) print flush
  687.  
  688.     } bind def
  689.  
  690. % ------ Reader for AFM files ------ %
  691.  
  692. % Dictionary for looking up character keywords
  693.    /cmdict 6 dict dup begin
  694.       /C { /c iarg def } def
  695.       /N { /n warg copystring def } def
  696.       /WX { /w narg def } def
  697.       /W0X /WX load def
  698.       /W /WX load def
  699.       /W0 /WX load def
  700.    end def
  701.  
  702.    /readAFM        % fontdict afmfilename readAFM -> fontdict
  703.     { (r) file /bdfile exch def
  704.       /Font exch def
  705.       /commentword (Comment) def
  706.  
  707. %  Check for the StartFontMetrics.
  708.       (StartFontMetrics) getline
  709.       args cvr 2.0 lt { (Not version 2.0 or greater\n) print stop } if
  710.  
  711. %  Look for StartCharMetrics, then parse the character metrics.
  712. %  The only information we care about is the X width.
  713.       /metrics 0 dict def
  714.        { nextline
  715.          keyword (EndFontMetrics) eq { exit } if
  716.      keyword (StartCharMetrics) eq
  717.       { iarg dup dict /metrics exch def
  718.          { /c -1 def /n null def /w null def
  719.            nextline buffer
  720.         { token not { exit } if
  721.           dup cmdict exch known
  722.            { exch /args exch def   cmdict exch get exec   args }
  723.            { pop }
  724.           ifelse
  725.         } loop
  726.            c 0 ge n null ne or w null ne and
  727.         { n null eq { /n Font /Encoding get c get def } if
  728.           metrics n w put
  729.         }
  730.            if
  731.          }
  732.         repeat
  733.         (EndCharMetrics) getline
  734.       } if
  735.        } loop
  736.  
  737. %  Insert the metrics in the font.
  738.        metrics length 0 ne
  739.     { Font /Metrics metrics readonly put
  740.     } if
  741.       Font
  742.     } bind def
  743.  
  744. end        % envBDF
  745.  
  746. % Enter the main program in the current dictionary.
  747. /bdfafmtops        % infilename afmfilename* outfilename fontname
  748.             %   encodingname uniqueID xuid
  749.  { envBDF begin
  750.      7 -2 roll exch 7 2 roll    % afm* in out fontname encodingname uniqueID xuid
  751.      readBDF        % afm* font
  752.      exch { readAFM } forall
  753.      save exch
  754.      dup /FontName get exch definefont
  755.      setfont
  756.      psfile writefont
  757.      restore
  758.      psfile closefile
  759.    end
  760.  } bind def
  761.  
  762. % If the program was invoked from the command line, run it now.
  763. [ shellarguments
  764.  { counttomark 4 ge
  765.     { dup 0 get
  766.       dup 48 ge exch 57 le and        % last arg starts with a digit?
  767.        { /StandardEncoding }        % no encodingname
  768.        { cvn }                % have encodingname
  769.       ifelse
  770.       exch (.) search            % next-to-last arg has . in it?
  771.        { mark 4 1 roll            % have xuid
  772.           { cvi exch pop exch (.) search not { exit } if }
  773.      loop cvi ]
  774.      3 -1 roll cvi exch
  775.        }
  776.        { cvi null            % no xuid
  777.        }
  778.       ifelse
  779.       counttomark 5 roll
  780.       counttomark 6 sub array astore
  781.       7 -2 roll cvn 7 -3 roll        % make sure fontname is a name
  782.       bdfafmtops
  783.     }
  784.     { cleartomark
  785.       (Usage:\n  bdftops xx.bdf [yy1.afm ...] zz.gsf fontname uniqueID [xuid] [encodingname]\n) print flush
  786.       mark
  787.     }
  788.    ifelse
  789.  }
  790. if pop
  791.